package com.feonix.common.util.xml;

import com.feonix.common.util.StringUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.tree.AbstractAttribute;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.dom4j.Dom4jXPath;

public class XmlDocument {
	private Log log = LogFactory.getLog(getClass());

	private String encoding = "utf-8";

	private Document document = null;

	private boolean isAppend = false;

	private File file = null;

	public XmlDocument(String encoding) {
		this.encoding = encoding;
	}

	public XmlDocument() {
	}

	public void read(String xmlString) {
		if (!StringUtil.isEmpty(xmlString)) {
			try {
				this.document = DocumentHelper.parseText(xmlString);
			} catch (DocumentException documentException) {
				this.log.error("create XmlDocument error", documentException);
			}
		}
	}

	public void readFromPath(String path) {
		if (!StringUtil.isEmpty(path)) {
			read(new File(path));
		}
	}

	public void read(File xmlFile) {
		this.file = xmlFile;
		if (xmlFile.exists()) {
			SAXReader sAXReader = new SAXReader();
			sAXReader.setValidation(false);
			sAXReader.setEncoding(this.encoding);
			try {
				this.document = sAXReader.read(xmlFile);
			} catch (DocumentException documentException) {
				this.log.error("create XmlDocument error", documentException);
			}
		}
	}

	public void createRoot(String nodeName) {
		if (StringUtil.isEmpty(nodeName)) {
			return;
		}
		if (this.document == null) {
			this.document = DocumentHelper.createDocument();
		}
		Element element = this.document.getRootElement();
		if (element == null) {
			this.document.addElement(nodeName);
		}
	}

	public XmlNode getXmlNode(String path) {
		XmlNode xmlNode = null;
		if (this.document == null || StringUtil.isEmpty(path)) {
			return xmlNode;
		}
		Node node = this.document.selectSingleNode(path);
		if (node != null) {
			xmlNode = createXmlNode(node);
		}
		return xmlNode;
	}

	public List<XmlNode> getXmlNodes(String path) {
		List<XmlNode> list1 = null;
		if (this.document == null || StringUtil.isEmpty(path)) {
			return list1;
		}
		List<Node> list2 = this.document.selectNodes(path);
		if (list2 != null) {
			list1 = new ArrayList<XmlNode>();
			XmlNode xmlNode = null;
			for (Node node : list2) {
				xmlNode = createXmlNode(node);
				list1.add(xmlNode);
			}
		}
		return list1;
	}

	public boolean isExist(String path) {
		boolean bool = false;
		if (this.document == null || StringUtil.isEmpty(path)) {
			return bool;
		}
		Node node = this.document.selectSingleNode(path);
		if (node != null) {
			bool = true;
		}
		return bool;
	}

	public boolean createPath(String path) {
		boolean bool = false;
		if (StringUtil.isEmpty(path)) {
			return bool;
		}
		Element rootElement = null;
		if (this.document == null) {
			this.document = DocumentHelper.createDocument();
		}
		rootElement = this.document.getRootElement();
		String[] pathArray = StringUtil.split(path, "/");
		if (pathArray == null) {
			return bool;
		}
		ArrayList<Element> arrayList = new ArrayList<Element>();
		for (int index = 0; index < pathArray.length; index++) {
			String str = pathArray[index];
			if (!StringUtil.isEmpty(str)) {
				arrayList.add(DocumentHelper.createElement(str));
			}
		}

		if (rootElement == null) {
			this.document.add(arrayList.get(0));
			rootElement = this.document.getRootElement();
		}
		if (rootElement.getName().equals((arrayList.get(0)).getName())) {
			arrayList.remove(0);
		}
		Collections.reverse(arrayList);
		Element element2 = null;
		for (Element element : arrayList) {
			if (element2 != null) {
				element.add(element2);
			}
			element2 = element;
		}
		rootElement.add(element2);
		return bool;
	}

	public boolean addNodeCDATA(String path, String nodeName, String value) {
		return addNode(path, nodeName, value, null, true);
	}

	public boolean addNode(String path, String nodeName, String value) {
		return addNode(path, nodeName, value, null, false);
	}

	public boolean addNode(String path, String nodeName, String value, Map<String, String> attrs, boolean isCdata) {
		boolean isSuccess = false;
		if (StringUtil.isEmpty(path) || StringUtil.isEmpty(nodeName)) {
			return isSuccess;
		}
		if (StringUtil.isEmpty(value)) {
			value = "";
		}
		if (this.document == null) {
			createRoot(path.split("/")[0]);
		}
		Element rootElement = (Element) this.document.selectSingleNode(path);
		if (rootElement == null) {
			createPath(path);
			rootElement = (Element) this.document.selectSingleNode(path);
			if (rootElement == null) {
				return isSuccess;
			}
		}
		Element element = DocumentHelper.createElement(nodeName);
		if (isCdata) {
			element.addCDATA(nodeName);
		} else {
			element.setText(nodeName);
		}
		if (attrs != null) {
			Set<Entry<String, String>> set = attrs.entrySet();
			for (Map.Entry<String, String> entry : set) {
				element.addAttribute(entry.getKey(), entry.getValue());
			}
		}
		rootElement.add(element);
		return true;
	}

	public boolean updateNodeCDATA(String path, String value) {
		return updateNode(path, value, null, true);
	}

	public boolean updateNode(String path, String value) {
		return updateNode(path, value, null, false);
	}

	public boolean updateNode(String path, String value, Map<String, String> attrs, boolean isCdata) {
		boolean bool = false;
		if (this.document == null || StringUtil.isEmpty(path)) {
			return bool;
		}
		if (StringUtil.isEmpty(value)) {
			value = "";
		}
		List<Element> list = this.document.selectNodes(path);
		if (list != null) {
			for (Element element : list) {
				element.clearContent();
				if (isCdata) {
					element.addCDATA(value);
				} else {
					element.setText(value);
				}
				if (attrs != null) {
					Set<Entry<String, String>> set = attrs.entrySet();
					for (Map.Entry<String, String> entry : set) {
						element.addAttribute(entry.getKey(), entry.getValue());
					}
				}
			}
			bool = true;
		}
		return bool;
	}

	public boolean removeNode(String path) {
		boolean bool = false;
		if (this.document == null || StringUtil.isEmpty(path)) {
			return bool;
		}
		List<Node> list = this.document.selectNodes(path);
		if (list != null) {
			for (Node node : list) {
				node.detach();
				this.document.remove(node);
			}
			bool = true;
		}
		return bool;
	}

	public boolean save() {
		if (this.file == null) {
			this.log.error("save error file is null");
			return false;
		}
		return saveAs(this.file);
	}

	public boolean saveAs(File file) {
		boolean bool = false;
		OutputFormat outputFormat = OutputFormat.createPrettyPrint();
		outputFormat.setEncoding(this.encoding);
		XMLWriter xmlWriter = null;
		try {
			if (!file.exists()) {
				FileUtils.touch(file);
			}
			xmlWriter = new XMLWriter(new OutputStreamWriter(new FileOutputStream(file, this.isAppend), this.encoding),
					outputFormat);
			xmlWriter.write(this.document);
			bool = true;
		} catch (Exception exception) {
			exception.printStackTrace();
			this.log.error("save error", exception);
		} finally {
			if (xmlWriter != null) {
				try {
					xmlWriter.close();
				} catch (IOException iOException) {
				}
			}
		}

		return bool;
	}

	private XmlNode createXmlNode(Node node) {
		XmlNode xmlNode = new XmlNode();
		xmlNode.setName(node.getName());
		xmlNode.setPath(node.getPath());
		xmlNode.setValue(node.getStringValue());
		xmlNode.setContent(node.getText());
		List<AbstractAttribute> list = ((Element) node).attributes();
		for (AbstractAttribute abstractAttribute : list) {
			xmlNode.putAttr(abstractAttribute.getName(), abstractAttribute.getStringValue());
		}
		return xmlNode;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	/**
	 * 
	 * @param path            节点路径
	 * @param attrName        某个属性的属性名
	 * @param attrValue       属性的属性值
	 * @param changeAttrName  要修改的属性的名
	 * @param changeAttrValue 要修改的属性的名
	 * @return
	 * @throws JaxenException 
	 */
	public boolean updateXmlNodeByAttr(String path, String attrName, String attrValue,
			String changeAttrName, String changeAttrValue) {
		boolean isSuccess = false;
		if(StringUtil.isEmpty(path)) {
			log.error("没有传入要修改的path路径");
			return isSuccess;
		}
		String[] pathArr = path.split("/");
		String chooseNode = pathArr[pathArr.length-1];
		Element rootElement = this.document.getRootElement();
		String xpathExpr = "//"+chooseNode+"[@"+ attrName+"='"+attrValue+"']";
		XPath xPath = null;
		List nodes = null;
		try {
			xPath = new Dom4jXPath(xpathExpr);
			nodes = xPath.selectNodes(this.document);
		} catch (Exception e) {
			log.error("updateXmlNodeByAttr error: ", e);
		}
		
		for(Object node : nodes) {
			Element element = (Element)node;
			Attribute attribute = element.attribute(changeAttrName);
			attribute.setValue(changeAttrValue);
		}
		return true;
	}
}
